package org.springblade.minio.utils;

import com.alibaba.fastjson.JSON;
import io.minio.*;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

@Slf4j
@Component
public class MinioUtils {
    @Value("${minio.endpoint}")
    private String endpoint;
    @Value("${minio.port}")
    private Integer port;
    @Value("${minio.accessKey}")
    private String accessKey;
    @Value("${minio.secretKey}")
    private String secretKey;
    @Value("${minio.bucketName}")
    private String bucketName;


    private static MinioClient minioClient;

    public MinioClient getInstance() {
        if (minioClient == null) {
            minioClient = MinioClient.builder().endpoint(endpoint, port, false).credentials(accessKey, secretKey).build();
        }
        return minioClient;
    }
    /**
     * 
     * @Description 获取minio所有的桶
     * @return java.util.List<io.minio.messages.Bucket>
     **/
    public List<Bucket> getAllBucket() throws Exception {
        // 获取minio中所以的bucket
        
        List<Bucket> buckets = getInstance().listBuckets();
        for (Bucket bucket : buckets) {
            log.info("bucket 名称:  {}      bucket 创建时间: {}", bucket.name(), bucket.creationDate());
        }
        return buckets;
    }


    /**
     *
     * @Description  上传到minio服务器
     * @param inputStream: 输入流
     * @param objectName: 对象名称
     * @return void
     **/
    public void uploadToMinio(InputStream inputStream, String objectName) {
        try {
            long size = inputStream.available();
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .stream(inputStream, size, -1)
                    .build();
            // 上传到minio
            getInstance().putObject(putObjectArgs);
            inputStream.close();
        } catch (Exception e) {
            log.error(e.getMessage());
            e.printStackTrace();
        }
    }

    /*
        获取指定bucketName下所有文件
     */
    public List<Object> getFolderList(String bucketName) throws Exception{
        Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).build());
        Iterator<Result<Item>> iterator = results.iterator();
        List<Object> items = new ArrayList<>();
        String format = "{'fileName':'%s','fileSize':'%s'}";
        while(iterator.hasNext()) {
            Item item = iterator.next().get();
            items.add(JSON.parse((String.format(format, item.objectName(),
                    formatFileSize(item.size())))));
        }
        return items;
    }


    /**
     *
     * @Description 根据指定的objectName获取下载链接，需要bucket设置可下载的策略
     * @param objectName: 对象的名称
     * @return java.lang.String
     **/
    public String getUrlByObjectName(String objectName) {
        String objectUrl = null;
        try {
            objectUrl = getInstance().getObjectUrl(bucketName, objectName);
        } catch (Exception e) {
            log.error(e.getMessage());
            e.printStackTrace();
        }
        return objectUrl;
    }
    /**
     * 按前缀批量下载文件到本地
     * @param prefix 文件前缀
     * @param saveDir 本地存储路径
     */
    @SneakyThrows
    public void downloadFiles(String prefix, String saveDir,String dirName) {
        Iterable<Result<Item>> objs = getInstance().listObjects(bucketName,prefix);
        for (Result<Item> result : objs) {
            String objectName = result.get().objectName();
            log.info("下载中==={}",objectName);
            ObjectStat statObject = getInstance().statObject(bucketName, objectName);
            if (statObject != null) {
                File file= new File(saveDir,"/"+objectName.replace(prefix,dirName+"/"));
                log.info("新建函数目录在{}", "/"+objectName.replace(prefix,dirName+"/"));
                File pfile= file.getParentFile();
                if(!pfile.exists())
                    pfile.mkdirs();
                minioClient.getObject(bucketName, objectName, file.getPath());
            }
        }
    }

    /**
     * 删除文件夹及文件
     * @param prefix 文件或文件夹名称
     */
    @SneakyThrows
    public void deleteObject(String prefix) {
        Iterable<Result<Item>> objs = getInstance().listObjects(bucketName,prefix);
        for (Result<Item> result : objs) {
            String objectName = result.get().objectName();
            minioClient.removeObject(RemoveObjectArgs.builder()
                    .bucket(bucketName).object(objectName).build());
        }
    }

	 /**
     * 
     * @Description 根据objectName从minio中下载文件到指定的目录
     * @param objectName: objectName
     * @param fileName: 文件名称
     * @param dir: 文件目录
     * @return void
     **/
    public void downloadFile(String objectName, String fileName, String dir) throws Exception {
        GetObjectArgs objectArgs = GetObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .build();
        File file = new File(dir);
        if (!file.exists()) {
            file.mkdirs();
        }
        InputStream inputStream = getInstance().getObject(objectArgs);
        FileOutputStream outputStream =  new FileOutputStream(new File(dir, fileName.substring(fileName.lastIndexOf("/")+1)));
        int length;
        byte[] buffer = new byte[1024];
        while ((length = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, length);
        }
        outputStream.close();
        inputStream.close();
    }

    private static String formatFileSize(long fileS) {
        DecimalFormat df = new DecimalFormat("#.00");
        String fileSizeString = "";
        String wrongSize = "0B";
        if (fileS == 0) {
            return wrongSize;
        }
        if (fileS < 1024) {
            fileSizeString = df.format((double) fileS) + " B";
        } else if (fileS < 1048576) {
            fileSizeString = df.format((double) fileS / 1024) + " KB";
        } else if (fileS < 1073741824) {
            fileSizeString = df.format((double) fileS / 1048576) + " MB";
        } else {
            fileSizeString = df.format((double) fileS / 1073741824) + " GB";
        }
        return fileSizeString;
    }
    /**
     *
     * @param theString
     * @return String
     */
    public static String unicodeToUtf8(String theString) {
        char aChar;
        int len = theString.length();
        StringBuffer outBuffer = new StringBuffer(len);
        for (int x = 0; x < len;) {
            aChar = theString.charAt(x++);
            if (aChar == '\\') {
                aChar = theString.charAt(x++);
                if (aChar == 'u') {
                    // Read the xxxx
                    int value = 0;
                    for (int i = 0; i < 4; i++) {
                        aChar = theString.charAt(x++);
                        switch (aChar) {
                            case '0':
                            case '1':
                            case '2':
                            case '3':
                            case '4':
                            case '5':
                            case '6':
                            case '7':
                            case '8':
                            case '9':
                                value = (value << 4) + aChar - '0';
                                break;
                            case 'a':
                            case 'b':
                            case 'c':
                            case 'd':
                            case 'e':
                            case 'f':
                                value = (value << 4) + 10 + aChar - 'a';
                                break;
                            case 'A':
                            case 'B':
                            case 'C':
                            case 'D':
                            case 'E':
                            case 'F':
                                value = (value << 4) + 10 + aChar - 'A';
                                break;
                            default:
                                throw new IllegalArgumentException(
                                        "Malformed   \\uxxxx   encoding.");
                        }
                    }
                    outBuffer.append((char) value);
                } else {
                    if (aChar == 't') {
						aChar = '\t';
					} else if (aChar == 'r') {
						aChar = '\r';
					} else if (aChar == 'n') {
						aChar = '\n';
					} else if (aChar == 'f') {
						aChar = '\f';
					}
                    outBuffer.append(aChar);
                }
            } else {
				outBuffer.append(aChar);
			}
        }
        return outBuffer.toString();
    }
}
